package com.jbookshelf.beans;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;

import org.primefaces.context.RequestContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.jbookshelf.dal.entities.Authors;
import com.jbookshelf.dal.entities.Books;
import com.jbookshelf.dal.entities.BooksGenres;
import com.jbookshelf.dal.entities.BooksGenresId;
import com.jbookshelf.dal.entities.Genres;
import com.jbookshelf.dal.entities.Languages;
import com.jbookshelf.dal.entities.Publishers;
import com.jbookshelf.dal.providers.AuthorsDataProvider;
import com.jbookshelf.dal.providers.BooksDataProvider;
import com.jbookshelf.dal.providers.BooksGenresProvider;
import com.jbookshelf.dal.providers.GenresDataProvider;
import com.jbookshelf.dal.providers.LanguagesDataProvider;
import com.jbookshelf.dal.providers.PublishersDataProvider;

@Component
@Scope("request")
public class BooksBean {
	@Autowired
	BooksDataProvider dataProvider;

	@Autowired
	AuthorsDataProvider authorsDataProvider;

	@Autowired
	GenresDataProvider genresDataProvider;

	@Autowired
	LanguagesDataProvider languageDataProvider;

	@Autowired
	PublishersDataProvider publishersDataProvider;

	@Autowired
	BooksGenresProvider bgDataProvider;

	private List<Books> books;
	private List<String> selectedTexts; // for genres's input
	private Books selectedBook;
	private Books newBook;
	private BooksGenres bookGenre;
	private String languageText; // for language's input
	private String authorText; // for author's input
	private String publisherText; // for publisher's input

	public String getLanguagetxt() {
		return languageText;
	}

	public void setLanguagetxt(String languagetxt) {
		this.languageText = languagetxt;
	}

	public String getAuthortxt() {
		return authorText;
	}

	public void setAuthortxt(String authortxt) {
		this.authorText = authortxt;
	}

	public String getPublishertxt() {
		return publisherText;
	}

	public void setPublishertxt(String publishertxt) {
		this.publisherText = publishertxt;
	}

	public List<String> getSelectedTexts() {
		return selectedTexts;
	}

	public void setSelectedTexts(List<String> selectedTexts) {
		this.selectedTexts = selectedTexts;
	}

	public BooksBean() {
		newBook = new Books();
	}

	public List<Books> getBooks() {
		if (books == null) {
			books = dataProvider.getBooks();
		}
		return books;
	}

	public Books getSelectedBook() {
		return selectedBook;
	}

	public void setSelectedBook(Books selectedBook) {
		this.selectedBook = selectedBook;
	}

	public void setBooks(List<Books> books) {
		this.books = books;
	}

	public void setName(String name) {
		String validName;
		validName = name.substring(0, 1).toUpperCase()
				+ name.substring(1).toLowerCase();
		this.newBook.setName(validName);
	}

	public String getName() {
		return newBook.getName();
	}

	public void setDate(Date date) {
		newBook.setPublishedOn(date);
	}

	public Date getDate() {
		return newBook.getPublishedOn();
	}

	public void setEdition(String edition) {
		newBook.setEdition(edition);
	}

	public String getEdition() {
		return newBook.getEdition();
	}

	public void setPages(int count) {
		newBook.setPagesCount(count);
	}

	public int getPages() {
		return newBook.getPagesCount();
	}

	public void setIsbn(String isbn) {
		newBook.setIsbn(isbn);
	}

	public String getIsbn() {
		return newBook.getIsbn();
	}

	public void setSummary(String summary) {
		newBook.setSummary(summary);
	}

	public String getSummary() {
		return newBook.getSummary();
	}

	public List<String> completeAuthors(String query) {
		List<String> suggestions = new ArrayList<String>();
		List<Authors> authors = authorsDataProvider.getAuthors();
		for (Authors a : authors) {
			// Something like correct comparison (both toLowerCase are
			// important)
			if (a.getFullName().toLowerCase().contains(query.toLowerCase()))
				suggestions.add(a.getFullName());
		}
		return suggestions;
	}

	private void setSelectedAuthor() {
		List<Authors> al = authorsDataProvider.getAuthors();
		Authors author = new Authors();
		for (Authors a : al) {
			if (a.getFullName().equals(authorText)) {
				author = a;
			}
		}
		System.out.println(author.getFullName());
		newBook.setAuthors(author);
	}

	public List<String> completeGenres(String query) {
		List<String> suggestions = new ArrayList<String>();
		List<Genres> genres = genresDataProvider.getGenres();
		for (Genres g : genres) {
			if (g.getName().toLowerCase().contains(query.toLowerCase()))
				suggestions.add(g.getName());
		}
		return suggestions;
	}

	private void setSelectedGenres(BooksGenres bg) {
		List<Genres> gl = genresDataProvider.getGenres();
		Set<BooksGenres> destGenres = new HashSet<BooksGenres>(0);
		for (Genres g : gl) {
			for (String s : selectedTexts) {
				if (g.getName().equals(s)) {
					bg.setGenres(g);
					bg.setId(new BooksGenresId(bg.getBooks().getId(), g.getId()));
					bgDataProvider.saveBooksGenres(bg);
				}
			}
		}
	}

	public List<String> completeLanguage(String query) {
		List<String> suggestions = new ArrayList<String>();
		List<Languages> languages = languageDataProvider.getLanguages();
		for (Languages l : languages) {
			if (l.getName().toLowerCase().contains(query.toLowerCase()))
				suggestions.add(l.getName());
		}
		return suggestions;
	}

	private void setSelectedLanguage() {
		List<Languages> ll = languageDataProvider.getLanguages();
		Languages lang = new Languages();
		for (Languages l : ll) {
			if (l.getName().equals(languageText)) {
				lang = l;
			}
		}
		System.out.println(lang.getName());
		newBook.setLanguages(lang);
	}

	public List<String> completePublisher(String query) {
		List<String> suggestions = new ArrayList<String>();
		List<Publishers> publishers = publishersDataProvider.getPublishers();
		for (Publishers p : publishers) {
			if (p.getName().toLowerCase().contains(query.toLowerCase()))
				suggestions.add(p.getName());
		}
		return suggestions;
	}

	private void setSelectedPublisher() {
		List<Publishers> pl = publishersDataProvider.getPublishers();
		Publishers publisher = new Publishers();
		for (Publishers p : pl) {
			if (p.getName().equals(publisherText)) {
				publisher = p;
			}
		}
		System.out.println(publisher.getName());
		newBook.setPublishers(publisher);
	}

	public void saveNewBook() {
		setSelectedAuthor();
		setSelectedLanguage();
		setSelectedPublisher();
		dataProvider.saveBook(newBook); // the book will be created, but
										// connection between book and genre
										// won't
		saveNewBooksGenres(); // so we have to make this connection
		hideCreateDialog();
		RequestContext.getCurrentInstance().update("books_list_form");
	}

	private void saveNewBooksGenres() {
		bookGenre = new BooksGenres();
		books = dataProvider.getBooks();
		Books lastBook = books.get(books.size() - 1);
		bookGenre.setBooks(lastBook);
		setSelectedGenres(bookGenre);
	}

	public void hideCreateDialog() {
		final RequestContext context = RequestContext.getCurrentInstance();
		context.execute("createdlg.hide()");
	}

	public void showCreateDialog() {
		// newUser = new Users();
		final RequestContext context = RequestContext.getCurrentInstance();
		context.execute("createdlg.show()");
	}

	public void showEditDialog() {
		if (books.contains(selectedBook)) {
			final RequestContext context = RequestContext.getCurrentInstance();
			context.execute("editdlg.show()");
		} else {
			FacesContext context = FacesContext.getCurrentInstance();
			context.addMessage(null, new FacesMessage("Failed!",
					"No book selected!"));
		}
	}

	public void showViewDialog() {
		if (books.contains(selectedBook)) {
			final RequestContext context = RequestContext.getCurrentInstance();
			context.execute("viewdlg.show()");
		} else {
			FacesContext context = FacesContext.getCurrentInstance();
			context.addMessage(null, new FacesMessage("Failed!",
					"No book selected!"));
		}
	}

	public void validateBookName(FacesContext context, UIComponent component,
			Object value) {
		String bookName = (String) value;
		if (dataProvider.getBookByName(bookName) != null) {
			throw new ValidatorException(new FacesMessage(
					FacesMessage.SEVERITY_ERROR,
					"This book's name already exist", null));
		}
	}

	public void validateBookAuthor(FacesContext context, UIComponent component,
			Object value) {
		String author = (String) value;
		List<Books> result = dataProvider.getBooksByAuthor(author);
		if (result.isEmpty()) {
			throw new ValidatorException(new FacesMessage(
					FacesMessage.SEVERITY_ERROR, "Author is not exist", null));
		}
	}

	public void validateBookLanguage(FacesContext context,
			UIComponent component, Object value) {
		String language = (String) value;
		List<Languages> result = languageDataProvider.getLanguages();
		boolean exist = false;
		for (Languages lang : result) {
			if (lang.getName().equals(language)) {
				exist = true;
				break;
			}
		}
		if (!exist) {
			throw new ValidatorException(new FacesMessage(
					FacesMessage.SEVERITY_ERROR, "Language is not exist", null));
		}
	}

	public void validateBookPublisher(FacesContext context,
			UIComponent component, Object value) {
		String publisher = (String) value;
		List<Publishers> result = publishersDataProvider.getPublishers();
		boolean exist = false;
		for (Publishers publish : result) {
			if (publish.getName().equals(publisher)) {
				exist = true;
				break;
			}
		}
		if (!exist) {
			throw new ValidatorException(
					new FacesMessage(FacesMessage.SEVERITY_ERROR,
							"Publisher is not exist", null));
		}
	}

	public void validateBookPagesCount(FacesContext context,
			UIComponent component, Object value) {
		Integer count = (Integer) value;

		if (count <= 0) {
			throw new ValidatorException(new FacesMessage(
					FacesMessage.SEVERITY_ERROR,
					"value cannot be less than or equal to zero", null));
		}
	}

	public void validateBookISBN(FacesContext context, UIComponent component,
			Object value) {
		String isbn = (String) value;
		if (!Pattern.compile("[0-9-]+").matcher(isbn).matches()) {
			throw new ValidatorException(new FacesMessage(
					FacesMessage.SEVERITY_ERROR, "Value has bad foramt", null));
		}
		List<String> isbnList = dataProvider.getISBNlist();
		if (isbn.contains(isbn))
		{
			throw new ValidatorException(new FacesMessage(
					FacesMessage.SEVERITY_ERROR, "ISBN already exist", null));
		}
	}
}
